Diabetes

Comprensión del negocio

La diabetes es una enfermedad crónica que afecta a la forma en que el cuerpo produce y utiliza la insulina. La insulina es una hormona que ayuda a las células a absorber la glucosa del torrente sanguíneo. Cuando el cuerpo no produce suficiente insulina o no puede utilizar la insulina de forma eficaz, los niveles de glucosa en sangre pueden aumentar.

La diabetes es una enfermedad muy común. Se estima que afecta a más de 422 millones de personas en todo el mundo. En los Estados Unidos, la diabetes es la séptima causa principal de muerte.

Hay dos tipos principales de diabetes:

  • Diabetes tipo 1: La diabetes tipo 1 es una enfermedad autoinmune en la que el cuerpo ataca las células productoras de insulina del páncreas. La diabetes tipo 1 suele diagnosticarse en niños y adultos jóvenes.
  • Diabetes tipo 2: La diabetes tipo 2 es la forma más común de diabetes. Se produce cuando el cuerpo no produce suficiente insulina o no puede utilizar la insulina de forma eficaz. La diabetes tipo 2 suele diagnosticarse en adultos mayores, pero está aumentando en niños y adultos jóvenes.

La diabetes puede causar una serie de complicaciones, que incluyen:

  • Enfermedades cardíacas: La diabetes es un factor de riesgo importante para las enfermedades cardíacas, que incluyen ataques cardíacos y accidentes cerebrovasculares.
  • Accidentes cerebrovasculares: Los accidentes cerebrovasculares son un tipo de ataque cerebral que pueden causar daño cerebral y discapacidad.
  • Enfermedad renal: La diabetes puede dañar los riñones, lo que puede conducir a la insuficiencia renal.
  • Enfermedad de las encías: La diabetes puede aumentar el riesgo de enfermedad de las encías, que puede provocar pérdida de dientes.
  • Neuropatía: La neuropatía es un daño a los nervios que puede causar hormigueo, entumecimiento y dolor en las manos, los pies y otras partes del cuerpo.
  • Problemas de visión: La diabetes puede dañar los vasos sanguíneos de los ojos, lo que puede provocar problemas de visión, como ceguera.
  • Muerte prematura: La diabetes puede aumentar el riesgo de muerte prematura.

El tratamiento de la diabetes tiene como objetivo controlar los niveles de glucosa en sangre para prevenir complicaciones. El tratamiento puede incluir dieta, ejercicio, medicamentos y, en algunos casos, insulina.

Hay una serie de empresas que operan en el negocio de la diabetes. Estas empresas incluyen fabricantes de medicamentos, dispositivos médicos y equipos de diagnóstico.

Los fabricantes de medicamentos para la diabetes producen una variedad de medicamentos que se utilizan para tratar la diabetes tipo 1 y la diabetes tipo 2. Estos medicamentos incluyen insulina, medicamentos orales y agentes de incretina.

Los fabricantes de dispositivos médicos para la diabetes producen una variedad de dispositivos que se utilizan para ayudar a las personas con diabetes a controlar sus niveles de glucosa en sangre. Estos dispositivos incluyen monitores de glucosa en sangre, bombas de insulina y sensores de glucosa en sangre.

Los fabricantes de equipos de diagnóstico para la diabetes producen una variedad de equipos que se utilizan para diagnosticar la diabetes. Estos equipos incluyen pruebas de glucosa en sangre y pruebas de tolerancia a la glucosa.

Y nosotros, los fabriantes de modelos predictivos para lograr identificar a los pacientes vulnerables a la diabetes y así poder prevenir las complicaciones que esta enfermedad puede causar.

El negocio de la diabetes es un mercado en crecimiento. Esto se debe a que la prevalencia de la diabetes está aumentando. Además, las personas con diabetes están viviendo más tiempo, lo que está aumentando el gasto en atención médica para la diabetes.

Objetivo

Desarrollar una aplicación en Shiny que permita a los usuarios identificar su riesgo de diabetes a partir de sus datos de salud.

Este objetivo es específico, medible, alcanzable, relevante y oportuno. Es específico porque se centra en el desarrollo de una aplicación en Shiny. Es medible porque se puede medir el número de usuarios que utilizan la aplicación y el número de personas que son diagnosticadas con diabetes. Es alcanzable porque se cuenta con las habilidades y los recursos necesarios para desarrollar la aplicación. Es relevante porque la aplicación tiene el potencial de ayudar a las personas a identificar su riesgo de diabetes. Es oportuno porque la diabetes es una enfermedad importante que afecta a millones de personas en todo el mundo.

Para alcanzar este objetivo, se plantean los siguientes pasos:

  1. Importar los conjuntos de datos de BloodPressure, Glucose, Oximetry y Weight.
  2. Integrar estas fuentes de datos usando R.
  3. Limpiar los datos para eliminar los outliers y las medidas disparatadas.
  4. Implementar el método de clustering para identificar el grupo vulnerable.
  5. Etiquetar este grupo y usar este dataset enriquecido para crear un árbol de clasificación.
  6. Desarrollar la aplicación en Shiny.

Comprensión de los datos

Se describirán los datos que se utilizarán para desarrollar la aplicación en Shiny sin buscar una relación directa entre ellos.

El objetivo será tener una vision general de los datos y su estructura, además de entender a qué se refiere cada variable.

Glucosa

La glucosa es el azúcar que el cuerpo utiliza como energía. Cuando los niveles de glucosa en sangre son altos, puede ser un signo de diabetes.

glucose <- read_csv("data/glucose.csv") 
glucose

Según los datos observados en la tabla, se cuenta con la toma de glucosa de pacientes, la fecha de la toma, el valor de la glucosa y el identificador del paciente.

Oximetria

La oximetría es una técnica para medir la cantidad de oxígeno en la sangre de una persona, generalmente a través de un dispositivo llamado oxímetro de pulso. Proporciona un porcentaje de saturación de oxígeno en la sangre, lo que es útil para evaluar la función respiratoria y la salud en general.

oximetry <- read_csv("data/Oximetry.csv", col_types = cols()) 
oximetry

Según los datos observados en la tabla, se cuenta con la toma de oximetría de pacientes con la fecha de la toma, y el identificador del paciente además de la toma de lpm (latidos por minuto).

Presión arterial

La presión arterial es la fuerza ejercida por la sangre contra las paredes de las arterias mientras el corazón bombea sangre alrededor del cuerpo.

La presión arterial normal se considera que es inferior a 120/80 mm Hg. Si la presión arterial es demasiado alta o demasiado baja, puede ser un signo de problemas de salud.

bloodPressure <- read_csv("data/bloodPressure.csv", col_types = cols())
bloodPressure

Según los datos observados en la tabla, se cuenta con la toma de presión arterial de pacientes con la fecha de la toma, la presion arterial sistólica (Systolic) y la presión arterial diastólica (Diastolic) ademas de la presión arterial media (Mean).

IMC


El indice de masa corportal es una medida de la grasa corporal basada en la altura y el peso de una persona. El IMC se utiliza a menudo para determinar si una persona tiene sobrepeso o es obesa. Se calcula dividiendo el peso en kilogramos por el cuadrado de la altura en metros.

El IMC es una herramienta útil para evaluar el riesgo de una persona de desarrollar problemas de salud relacionados con el peso, como enfermedades cardíacas, diabetes y algunos tipos de cáncer.

imc <- read_csv("data/Weight_Height.csv", col_types = cols())
imc

Según los datos observados en la tabla, se cuenta con la toma de peso y altura de los pacientes, la fecha de la toma, y el calculo del IMC.

Exploración de los datos


En este punto se busca entender la distribución de los datos y la correlación entre ellos. Tratar de encontrar patrones, identificar outliers y transformar los datos a una forma más conveniente.

Glucosa

Valores faltantes

glucose %>%
  summarise_all(~ sum(is.na(.))) %>%
  gather() %>%
  kable() %>%
  kable_styling(full_width = F, position = "left")
key value
Patient 0
Glucose 0
Date 0

En la tabla anterior se puede observar que no hay valores faltantes en el dataset de glucosa.

Summary


Los datos del resumen del dataset de glucosa sugieren que la mayoría de los pacientes tienen valores de glucosa normales. Sin embargo, hay algunos pacientes con valores de glucosa más altos de lo normal. Por ejmplo 207. Como tambien se identifican pacientes cerca de la hipoglucemia (valores de glucosa muy bajos).

kable(summary(glucose)) %>% kable_styling()
Patient Glucose Date
Min. : 1.0 Min. : 51.0 Length:10074
1st Qu.: 426.0 1st Qu.: 82.0 Class :character
Median : 736.0 Median :102.0 Mode :character
Mean : 726.2 Mean :105.6 NA
3rd Qu.:1076.0 3rd Qu.:127.0 NA
Max. :1453.0 Max. :207.0 NA

Distribución


La distribución de los datos parece ser un poco simétrica y se puede observar que la mayoria de los pacientes tienen un valor de glucosa entre 80 y 110. Además, hay una cola más larga a la derecha, lo que indica que hay algunos pacientes con valores de glucosa más altos de lo normal.

p <- p <- ggplot(glucose, aes(x = Glucose)) +
  geom_histogram(binwidth = 10,color="#0A2F35", fill="#12492F", alpha=0.7) +
  labs(x = "Glucosa", y = "Frecuencia") +
  theme_clean() +
  ggtitle("Distribución de glucosa")

ggplotly(p)


Las tomas de glucosa se suelen tomar en la mañana y en la tarde. Se espera que los valores de glucosa sean más bajos en la mañana y más altos en la tarde. El siguiente gráfico confirma esta hipotesis.

glucose$IsMorning <- ifelse(is_morning(glucose$Date), "Morning", "Afternoon")
glucose$IsMorning <- factor(glucose$IsMorning)

ggplotly(ggplot(glucose, aes(x = Glucose, fill = IsMorning)) +
  geom_histogram(binwidth = 10, alpha=0.7, color="#0A2F35") +
  facet_grid(IsMorning ~ .) +
  labs(x = "Glucosa", y = "Frecuencia") +
  ggtitle("Distribución de glucosa por la mañana y por la tarde") +
  theme_fivethirtyeight())


El siguiente gráfico muestra las tomas de los pacientes en la mañana con una marca en el limite de lo que se considera normal. Los valores a la derecha de la marca se consideran altos y se considerarían vulnerables a diabetes.

morning_critir_value <- 100

ggplotly(ggplot(glucose %>% filter(IsMorning=="Morning"), aes(x = Glucose)) +
  geom_histogram(binwidth = 10, alpha=0.7, color="#0A2F35", fill="#12492F") +
  labs(x = "Glucosa", y = "Frecuencia") +
  ggtitle("Distribución de glucosa en la mañana") +
  geom_vline(xintercept = morning_critir_value, color = "red", linetype = "dashed", size = 1, show.legend = TRUE) +
  theme_fivethirtyeight())


El siguiente gráfico muestra las tomas de los pacientes en la tarde con una marca en el limite de lo que se considera normal. Los valores a la derecha de la marca se consideran altos y se considerarían vulnerables a diabetes.

afternoon_critir_value <- 180

ggplotly(ggplot(glucose %>% filter(IsMorning=="Afternoon"), aes(x = Glucose)) +
           geom_histogram(binwidth = 10, alpha=0.7, color="#0A2F35", fill="#12492F") +
           labs(x = "Glucosa", y = "Frecuencia") +
           ggtitle("Distribución de glucosa en la tarde") +
           geom_vline(xintercept = afternoon_critir_value, color = "red", linetype = "dashed", size = 1, show.legend = TRUE) +
           theme_fivethirtyeight())
glucose <- glucose %>% select(-IsMorning)

Outliers


En el siguiente gráfico se identifican ciertas tomas de glucosa que se considerarian outliers. Sin embargo, no son datos erroneos ya que es posible que los pacientes tengan valores de glucosa muy altos o muy bajos. Además, representan información importante para identificar a los pacientes vulnerables a diabetes.

p <- ggplot(glucose, aes(y = Glucose)) +
  geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
  theme_clean() +
  ggtitle("Análisis de Outliers en Glucosa")

ggplotly(p)

Transformación


Se valida que los datos de la fecha esten en el formato correcto.

glucose <- glucose %>%
  mutate(Date = parse_date_time(Date, orders = "mdy HM"))

sum(is.na(glucose$Date))
## [1] 0


La función parse_date_time() de la biblioteca dplyr intenta convertir una caedna de caracteres a una fecha y hora. Si la conversión no es exitosa, la función devuelve un valor NA (formato incorrecto). Con el resultado obtenido se puede concluir que todas la fechas se convirtieron con éxito y tienen el formato correcto.

Oximetria

Valores faltantes

oximetry %>%
  summarise_all(~ sum(is.na(.))) %>%
  gather() %>%
  kable() %>%
  kable_styling(full_width = F, position = "left")
key value
Patient 0
SpO2 0
HeartRate 0
Date 0

En la tabla anterior se puede observar que no hay valores faltantes en el dataset de oximetria.

Summary

El resument de los datos indica varias cosas. La Sp02 media de los pacientes es de 83 lo que indica que la mayoria de los pacientes tienen una saturación de oxígeno baja. Además, la frecuencia cardiaca media es de 80 lo que indica que la mayoria de los pacientes tienen una frecuencia cardiaca normal.

kable(summary(oximetry)) %>% kable_styling()
Patient SpO2 HeartRate Date
Min. : 1.0 Min. : 44.00 Min. : 44.00 Length:9872
1st Qu.: 438.0 1st Qu.: 71.00 1st Qu.: 66.00 Class :character
Median : 749.0 Median : 83.00 Median : 80.00 Mode :character
Mean : 739.8 Mean : 81.94 Mean : 80.36 NA
3rd Qu.:1082.0 3rd Qu.: 94.00 3rd Qu.: 94.00 NA
Max. :1453.0 Max. :100.00 Max. :217.00 NA

Distribución


La distribución de la oximetria sugiere que hay un numero significativo de pacientes que tienen una saturación de oxígeno baja la cual es menos de 90. En general, la distribución de oximetría es asimetrica ya que hay más pacientes con una saturación de oxígeno baja(por debajo de 90) que con una saturación de oxígeno normal (arriba de 90).


ggplotly(ggplot(oximetry, aes(x = SpO2)) +
  geom_histogram(binwidth = 10,color="#0A2F35", fill="#12492F", alpha=0.7) +
  labs(x = "Oximetria", y = "Frecuencia") +
  theme_clean() +
  ggtitle("Distribución de oximetria")
)


La distribución de la frecuencia cardiaca es levemente simétrica (excluyendo los outliers) y se puede observar que la mayoria de los pacientes tienen una frecuencia cardiaca entre 60 y 100. Hay ciertos pacientes con una frecuencia cardiaca muy alta (más de 120).


ggplotly(ggplot(oximetry, aes(x = HeartRate)) +
           geom_histogram(binwidth = 10,color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "HeartRate", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Distribución de frecuencia cardiaca")
)

Correlación de variables


La correlacion entre la oximetría y la frecuencia cardiaca es positiva pero muy moderada. Lo que significa que en algunas ocaciones cuando la oximetría aumenta, la frecuencia cardiaca también aumenta. En general, no es una relacion directa y puedes haber otros factores que afecten los resultados de las muestras.


ggplotly(ggcorrplot(cor(select(oximetry, -Patient, -Date))))
ggpairs(
  select(oximetry, -Patient, -Date),
  columns = c("SpO2", "HeartRate"),
  title = "Gráfico de correlación entre variables",
)

Outliers


El boxplot de la oximetria apesar de que muestra algunos datos muy preocupantes con indicios de enfermedades pulmonares o cardiovasculares, no se consideran outliers ya que es posible que los pacientes tengan una saturación de oxígeno muy baja o muy alta.


ggplotly(ggplot(oximetry, aes(y = SpO2)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "Oximetria", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en oximetria")
)


En el siguiente gráfico se puede observar que hay algunos pacientes con una frecuencia cardiaca muy alta (más de 110) y esto puede tener relación con problemas de posible diabetes ya que la hipoglusemia puede causar taquicardia.


ggplotly(ggplot(oximetry, aes(y = HeartRate)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "HeartRate", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en frecuencia cardiaca")
)

Transformación

Segun los análisis de los boxplot de oximetría, no se evidencian outliers por lo cual solo se valida el formato de la fecha.

oximetry <- oximetry %>%
  mutate(Date = parse_date_time(Date, orders = "mdy HM"))

Preguntas de negocio

¿Hay algún paciente con una SpO2 o frecuencia cardíaca que haya cambiado significativamente con el tiempo?

Presión arterial

Valores faltantes

bloodPressure %>%
  summarise_all(~ sum(is.na(.))) %>%
  gather() %>%
  kable() %>%
  kable_styling(full_width = F, position = "left")
key value
Patient 0
Systolic 0
Diastolic 0
AvBloodPressure 0
HeartRate 0
Date 0

En la tabla anterior se puede observar que no hay valores faltantes en el dataset de presión arterial.

Summary

kable(summary(bloodPressure)) %>% kable_styling()
Patient Systolic Diastolic AvBloodPressure HeartRate Date
Min. : 1.0 Min. : 0.0 Min. : 0.00 Min. : 0.0 Min. : 0 Length:10167
1st Qu.: 420.0 1st Qu.: 99.0 1st Qu.: 63.00 1st Qu.: 92.0 1st Qu.: 67 Class :character
Median : 730.0 Median :113.0 Median : 74.00 Median :108.0 Median : 80 Mode :character
Mean : 720.2 Mean :112.8 Mean : 73.38 Mean :110.5 Mean : 80 NA
3rd Qu.:1073.0 3rd Qu.:127.0 3rd Qu.: 84.00 3rd Qu.:129.0 3rd Qu.: 93 NA
Max. :1453.0 Max. :187.0 Max. :118.00 Max. :149.0 Max. :183 NA

Distribución

ggplotly(ggplot(bloodPressure, aes(x = Systolic)) +
           geom_histogram(binwidth = 10,color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "Systolic", y = "Frecuencia") +
           theme_clean() +
           geom_vline(xintercept = 120, color = "red", linetype = "dashed", size = 1, show.legend = TRUE) +
           ggtitle("Distribución de Systolic"))
ggplotly(ggplot(bloodPressure, aes(x = Diastolic)) +
           geom_histogram(binwidth = 10,color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "Diastolic", y = "Frecuencia") +
           theme_clean() +
           geom_vline(xintercept = 80, color = "red", linetype = "dashed", size = 1, show.legend = TRUE) +
           ggtitle("Distribución de Diastolic"))
ggplotly(ggplot(bloodPressure, aes(x = HeartRate)) +
           geom_histogram(binwidth = 10,color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "HeartRate", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Distribución de HeartRate"))

Correlación de variables

ggplotly(ggcorrplot(cor(select(bloodPressure, -Patient, -Date))))
ggpairs(
  select(bloodPressure, -Patient, -Date),
  columns = c("Systolic", "Diastolic", "AvBloodPressure", "HeartRate"),
  title = "Gráfico de correlación entre variables",
)

Outliers


La grafica de la presion arterial sistolica muestra que hay algunos pacientes con una presion arterial sistolica muy alta (más de 170). La relación entre la presión arterial sistólica y la diabetes es compleja. La diabetes puede causar una serie de cambios en el cuerpo que pueden conducir a la presión arterial alta por lo cual no se consideran outliers.

Tambien se identifica un paciente con una presión arterial sistólica en 0 lo cual es imposible y se considera un outlier.


ggplotly(ggplot(bloodPressure, aes(y = Systolic)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "Systolic", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en Systolic")
)


La grafica de la presion arterial diastolica muestra que hay algunos pacientes con algunos valores atipicos. Hay un paciente con una muestra de presión arterial diastólica en 0 lo cual es imposible y se considera un outlier.

Tambien, hay un paciente con una presión arterial diastólica muy alta en comparación al resto (más de 115) pero no se considera un outlier ya que la diabetes puede causar una serie de cambios en el cuerpo que pueden conducir a la presión arterial alta.


ggplotly(ggplot(bloodPressure, aes(y = Diastolic)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "Diastolic", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en Diastolic")
)


En el siguiente gráfico se puede observar que apesar que hay algunas tomas preocupantes, se encuentran dentro del rango normal en la distribución de los datos. Sin embargo, hay un paciente con una presión arterial de 0 lo cual es imposible y se considera un outlier.


ggplotly(ggplot(bloodPressure, aes(y = AvBloodPressure)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "AvBloodPressure", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en AvBloodPressure")
)


La grafica de la frecuencia cardiaca muestra que hay algunos pacientes con una frecuencia cardiaca muy alta en comparación a las demás muestras(más de 125), pero como se mencionó en los gráficos anteriores, la hipoglusemia puede causar taquicardia y no se consideran outliers.

Sin embargo, tambien se encuentra una muestra con valor 0 lo cual es imposible y se considera un outlier.


ggplotly(ggplot(bloodPressure, aes(y = HeartRate)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "HeartRate", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en HeartRate")
)

Transformación

Seguún los análisis en los gráficos anteriores, se identificaron tomas de presión arterial sistólica, diastólica y media en 0.

bloodPressure %>% filter(Systolic == 0 | Diastolic == 0 | AvBloodPressure == 0 | HeartRate == 0)


Para los 4 gráficos anteriores, se identifica que el outlier es el mismo paciente. Por lo cual se decide eliminar esa toma para ese paciente del dataset ya que podría deberse a un error de medición o un error al ingresar los datos.

Tambien se identifica el campo AvBloodPreasure que es el promedio de la preción arterial, no tiene el valor correcto por lo cual se recalcula.


bloodPressure <- bloodPressure %>% filter(Systolic != 0 | Diastolic != 0 | AvBloodPressure != 0 | HeartRate != 0)
bloodPressure <- bloodPressure %>%
    mutate(Date = parse_date_time(Date, orders = "mdy HM")) %>%
    filter(HeartRate > 0)

IMC

imc %>%
  summarise_all(~ sum(is.na(.))) %>%
  gather() %>%
  kable() %>%
  kable_styling(full_width = F, position = "left")
key value
Patient 0
Weight 0
Height 0
IMC 0
Date 0

Summary

kable(summary(imc)) %>% kable_styling()
Patient Weight Height IMC Date
Min. : 1.0 Min. : -9.70 Min. :1.450 Min. :-4.20 Length:10170
1st Qu.: 418.2 1st Qu.: 65.37 1st Qu.:1.600 1st Qu.:21.61 Class :character
Median : 730.0 Median : 80.00 Median :1.720 Median :26.91 Mode :character
Mean : 719.6 Mean : 81.17 Mean :1.726 Mean :27.83 NA
3rd Qu.:1072.0 3rd Qu.: 98.85 3rd Qu.:1.850 3rd Qu.:33.41 NA
Max. :1453.0 Max. :172.00 Max. :6.520 Max. :56.56 NA

Distribución

p <- p <- ggplot(imc, aes(x = IMC)) +
  geom_histogram(binwidth = 10,color="#0A2F35", fill="#12492F", alpha=0.7) +
  labs(x = "IMC", y = "Frecuencia") +
  theme_clean() +
  ggtitle("Distribución de IMC")

ggplotly(p)

Correlación

ggplotly(ggcorrplot(cor(select(imc, -Patient, -Date))))
ggpairs(
  select(imc, -Patient, -Date),
  title = "Gráfico de correlación entre variables",
)

Outliers

ggplotly(ggplot(imc, aes(y = IMC)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "IMC", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en IMC")
)
ggplotly(ggplot(imc, aes(y = Weight)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "Weight", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en peso")
)
ggplotly(ggplot(imc, aes(y = Height)) +
           geom_boxplot(color="#0A2F35", fill="#12492F", alpha=0.7) +
           labs(x = "Height", y = "Frecuencia") +
           theme_clean() +
           ggtitle("Análisis de Outliers en altura")
)

Transformación

imc <- imc %>%
    mutate(Date = parse_date_time(Date, orders = "mdy HM")) %>%
    # todo: argumentar estos filtros
    filter(Height < 3) %>%
    filter(Weight > 15) %>%
    filter(IMC > 0)

Union de datasets

En este punto se realiza la unión de los datos por el método inner join. El join escogido se debe a que al ser una predicción a un tema tan delicado como es la diabetes, se requiere tener la mayor cantidad de datos por paciente para lograr una mayor confianza en los resiltados. Si alguno de los pacientes no cuenta con alguno de los examenes, simplemente se será excluido.

diabetes <- glucose %>%
  inner_join(bloodPressure, by = c("Patient", "Date")) %>%
  inner_join(oximetry, by = c("Patient", "Date")) %>%
  inner_join(imc, by = c("Patient", "Date")) %>%
  arrange(Patient, Date) 

nrow(diabetes)
## [1] 8356

Transformación

Se modifica el campo Date a timestamp para trabajarlo como numero ya que los modelos de clustering utilizan las distancias de los datos para lograr agruparlos.

diabetes <- diabetes %>%
    mutate(Date = as.numeric(Date)) %>%
    mutate(HeartRate = (HeartRate.x + HeartRate.y) / 2) 
ggplotly(ggcorrplot(cor(diabetes), title = "Correlacion general"))

Las variables con alta correlación pueden indicar varias cosas. Redundancia en la información, problemas de colinealidad y overfitting. En general el modelo puede intentar ajustarse demasiado a la relación negativa y/o positiva, lo que puede llevar a la falta de generalización.

Debido a esto, se proceden a eliminar las variables con alta correlación.

Se omiten campos innecesarios como el Id del paciente y las variables que ya se encuentra resumidas en otras variables.

diabetes <- diabetes %>%  
  # La presión arterial promedio se encuentra mal calculada por lo que se realiza la operación nuevamente. 
  mutate(AvBloodPressure = Diastolic + (1/3) * (Systolic - Diastolic)) %>%
  
  # Eliminación de variables con alta correlación
  select(-Patient, -HeartRate.x, -HeartRate.y, -Weight , -Height, -Systolic, -Diastolic, -Date)
ggplotly(ggcorrplot(cor(diabetes), title = "Correlacion general"))

Estandarización

preProcessDiabetes <- preProcess(diabetes, method = c("scale"))
scaled <- predict(preProcessDiabetes,diabetes)
diabetes_scaled <- as.data.frame(scaled)

PCA

El objetivo de usar PCA en este punto de debe a la capacidad que tiene la función para reducir la complejidad de los datos. PCA reduce la dimensionalidad del conjunto de datos original al transformarlo en un espacio de características de menor dimensión.

Esto puede facilitar el modelo de clustering y mejorar la precisión.

res.pca <- PCA(diabetes_scaled, graph = FALSE, ncp = 2)

La función fviz_pca_var tiene como objetivo visualizar las contribuciones de las variables originales a los componentes principales generados por el PCA en su primera dimensión. En el grafico se puede observar que las variables que más contribuyen a la primera dimensión son la glucosa y la presión arterial y la oximetría.

fviz_pca_var(res.pca, col.var="contrib") +
  scale_color_gradient2(low="blue", mid="white", high="red", midpoint=55) + 
  theme_clean()

Se obtienen los valores de los componentes principales para cada paciente, los cuales serán utilizados para el clustering.

diabetes_pca <- res.pca$ind$coord

Modelado Clustering

Determinar numero de clusters

La función fviz_nbclust permite determinar el numero optimo de clusters en un proceso de clustering utilizando diferentes métodos de evaluación de clústeres, como el método el método de la silueta (silhouette).

ggplotly(fviz_nbclust(diabetes_pca, kmeans, method = "silhouette", k.max = 10))

K- means

Según lo sugiere el grafico anterior, el numero de clusters optimos es 2 por lo cual se configura el parámetro k con dicho valor.

model <- kmeans(diabetes_pca, 2, iter.max = 100, algorithm = "Lloyd", nstart = 10)

Grafico del clustering

fviz_cluster(model, diabetes_pca, ellipse.type = "norm")

Asignación de clusters

Se asignan los clusters determinados por el modelo no supervidado a los diferentes pacientes del dataset.

diabetes_with_clusters <- cbind(diabetes, Cluster = factor(model$cluster))

Evaluación Clustering

El coeficiente de varianza explicada es una medida de la proporción de la varianza total de los datos que se explica por los clusters. Un coeficiente de varianza explicada alto indica que los clusters explican una gran parte de la variabilidad de los datos.

coeficiente de varianza explicada = (varianza entre clusters) / (varianza total)

model$betweenss / model$tot.withinss
## [1] 0.51034

Selección de grupo vulnerable

 

Uno de los principales factores de diabetes es la obesidad ya que el exceso de grasa dificulta que la insulina funcione correctamente, lo que lleva a niveles altos de azúcar en la sangre y, finalmente, a la diabetes.

Por lo cual se decide seleccionar el grupo vulnerable a diabetes basado en el IMC de los pacientes. En el siguiente grafico se puede observar que el grupo 1 tiene un IMC más alto que el grupo 2.

Tambien podriamos considerar la glucosa como un factor de selección pero el IMC es un factor más importante ya que la glucosa puede variar dependiendo de la hora del día en que se toma la muestra.

ggplotly(ggplot(as.data.frame(diabetes_with_clusters), aes(x=IMC, color=Cluster)) +
  geom_histogram() +
  facet_grid(~Cluster) + 
  geom_vline(xintercept = 30, color = "red", linetype = "dashed", size = 1, show.legend = TRUE) +
  ggtitle("Agrupación de la glucosa en los pacientes")) 

Clasificación

Split data

particion <- createDataPartition(diabetes_with_clusters$Cluster, p = 0.7, list = FALSE)
train <- diabetes_with_clusters[particion, ]
test <- diabetes_with_clusters[-particion, ]

Crear modelo

tree_model <- rpart(Cluster ~ ., data = train, method = "class", control = rpart.control(maxdepth = 3, minsplit = 2))

Arbol

prp(tree_model)

Curva de complejidad

plotcp(tree_model)

Predicción

y_pred <- predict(tree_model, newdata = test, type = "class")

Evaluación

y_pred_cluster <- factor(y_pred, levels = levels(test$Cluster))
accuracy(test$Cluster, y_pred_cluster)
## [1] 0.8695132
cf <- confusionMatrix(test$Cluster, y_pred_cluster)
fourfoldplot(as.table(cf),color=c("red","green"),main = "Confusion Matrix")

Exportar

saveRDS(tree_model, "diabetes.rds")

Conclusiones

El modelo de clustering logró identificar dos grupos de pacientes con características similares pero con diferentes niveles de riesgo de diabetes.

Además de la identificación de dos grupos de pacientes con diferentes niveles de riesgo de diabetes, el análisis podría haber revelado algunos factores de riesgo específicos que contribuyen a la vulnerabilidad de ciertos pacientes. Estos factores podrían incluir el índice de masa corporal (IMC) y la oximetría. Identificar estos factores podría ser valioso para desarrollar estrategias de prevención y detección temprana.

Despues de la investigación realizada, se concluye que detectar la diabetes no es una tarea tribial ya que muchas muestras dependen de la hora del día en que se toman y de la alimentación del paciente.